/******************************************************************************* 
  Copyright (C), 1988-2020, zeno Tech. Co., Ltd.
  Description:   ENC模块  
  Function List:     
  History:
    <huangzhendi -- 2014-09-26>  创建
*******************************************************************************/ 

/*=============================================================================
                                 Includes                                  
 =============================================================================*/
#include "videoPriv.h"
#include "videoIsp.h"
// #include "vdo_OsdMenuInterface.h"

/*=============================================================================
                               Macro Definition
 =============================================================================*/
#define VENC_STATTIME 10
#define CALL_BACK
//#define VENC_COUNTTIME

static __inline unsigned long long GetTickCount()
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ((unsigned long long)ts.tv_sec * 1000L + ts.tv_nsec / 1000000);
}

unsigned long long vdo_get_time()
{
    return GetTickCount();/* ms */
}

#ifndef MIN
#define MIN(a,b)  ((a) > (b) ? (b) : (a))
#endif
#ifndef MAX
#define MAX(a,b)  ((a) < (b) ? (b) : (a))
#endif


/*=============================================================================
                        Local Structures and Typedef 
 =============================================================================*/
typedef struct
{
    Rect_ext rect;                         /* 抠图位置 */
    MEDIA_BUFFER srcMb;
    MEDIA_BUFFER cropMb;
    MEDIA_BUFFER resizeMb;
    MEDIA_BUFFER stitchMb;
    unsigned int splitWidth;                      /* 目标宽 */
	unsigned int splitHeight;                     /* 目标高 */
	unsigned int stitchType;
	unsigned int stitchCnt;
	unsigned int isSpeak;
}ENC_stitchPrm_t;

/*=============================================================================
                             Extern Variables                                  
=============================================================================*/
typedef enum
{
    ENC_MAIN_STREAM,                /* 主码流 */
    ENC_SUB_STREAM,                 /* 辅码流1 */
    ENC_SUB_STREAM2,                /* 辅码流2 */
    ENC_SNAPSHOT_STREAM,            /* 抓图码流 */
    ENC_ANALYSESNAP_STREAM,        /* 智能抓图码流 */
    ENC_ANALYSESNAP_STREAM2,        /* 智能抓图码流2 */
    ENC_UNSUPPORT_STREAM,           /* 无效码流 */
} ENC_streamType_t;


typedef enum
{
    ENC_STOP,
    ENC_START_EMPTY,
    ENC_START_FULL,
} ENC_streamState_t;


int ENC_Rgb8888Mirror(BITMAP_S *pstBitmap)
{
    unsigned int width;
    unsigned int height;
    unsigned int *pu32Data;
    unsigned int tmp;
    unsigned int w, h;
    
    pu32Data = pstBitmap->pData;
    width = pstBitmap->u32Width;
    height = pstBitmap->u32Height;

    for(h = 0; h < height; h++)
    {
        for (w = 0; w < width/2; w++)
        {
            tmp = pu32Data[width*(h+1) - w - 1];
            
            pu32Data[width*(h+1) - w - 1] = pu32Data[w + h*width];
            pu32Data[w + h*width] = tmp;
        }
    }
    return 0;
}

int ENC_Rgb8888MirrorV2(BITMAP_S *pstDstBitmap,BITMAP_S *pstSrcBitmap)
{
    unsigned int *pu32DstData = NULL;
    unsigned int *pu32SrcData = NULL;

    unsigned int width  = 0;
    unsigned int height = 0;
    unsigned int tmp    = 0;
    unsigned int w = 0;
    unsigned int h = 0;

    pu32DstData = pstDstBitmap->pData;
    pu32SrcData = pstSrcBitmap->pData;

    width       = pstDstBitmap->u32Width;
    height      = pstDstBitmap->u32Height;

    for(h = 0; h < height; h++)
    {
        for (w = 0; w < width; w++)
        {
            tmp = pu32SrcData[width*(h+1) - w - 1];

            pu32DstData[w + h*width] = tmp;
        }
    }
    return 0;
}

int ENC_rgba8888_ToYuv420Nv12(Uint8* rgbBuf,Uint8* yuvBuf, Uint32 nWidth, Uint32 nHeight, Uint32 len)
{
    int iRet = 0;
    int i=0, j=0;
    Uint8 *bufY=NULL, *bufUV=NULL, *bufRGB=NULL;
    Uint8 y, u, v, a, r, g, b;
    Uint32 ylen = nWidth * nHeight;

    if ( (NULL == rgbBuf) || (NULL == yuvBuf) )
    {
        return -1;
    }

    memset(yuvBuf,0x0,len);
    bufY = yuvBuf;
    bufUV = yuvBuf + nWidth * nHeight;

    for (j = 0; j<nHeight;j++)
    {
        bufRGB = rgbBuf + nWidth * j  * 4 ;
        for (i = 0;i<nWidth;i++)
        {
            int pos = nWidth * i + j;
             // r, g, b, a   
            //a = *(bufRGB++);
            r = *(bufRGB++);
            g = *(bufRGB++);
            b = *(bufRGB++);
            a = *(bufRGB++);
            

            y = (unsigned char)( ( 66 * r + 129 * g +  25 * b + 128) >> 8) + 16;
            u = (unsigned char)( ( -38 * r -  74 * g + 112 * b + 128) >> 8) + 128;
            v = (unsigned char)( ( 112 * r -  94 * g -  18 * b + 128) >> 8) + 128;

            //save Y
            *(bufY++) = MAX( 0, MIN(y, 255 ));
            if (j%2==0&&i%2 ==0)
            {
                // save U .......
                *(bufUV++) = MAX( 0, MIN(u, 255 ));
                *(bufUV++) = MAX( 0, MIN(v, 255 ));
            }
    #if 0
            if(0==j && i<=10 && i >= 0)
               OSA_Printf("====>>>zhengyc: argb2Yuv420_%d_%d src:r,g,b(%x,%x,%x)   dst:y,u,v:(%x,%x,%x)\n",j,i,r,g,b,y,u,v);
    #endif
        }
     }

     return iRet;
}

int ENC_Rgb8888ToYUV2(BMP_logoInfo_t *pLogoInfo,BITMAP_S *pstBitmap)
{
	unsigned int * pData = pstBitmap->pData;
	unsigned char *yuvData;
	
	unsigned int width = pstBitmap->u32Width;//VIDEO_ALIGN_DOWN(pstBitmap->u32Width, 4); //
	unsigned int height = pstBitmap->u32Height;//VIDEO_ALIGN_DOWN(pstBitmap->u32Height, 4);//
	
	unsigned int a, r, g, b, nsize;
	unsigned char *y, *uv, *tmpu, *tmpv;

	int w, h, k;

	//FILE* fp;
	//fp = fopen("/tmp/nfs/1.yuv", "w+");

	LOG_PrintInfo("enter ENC_Rgb8888ToYUV,width=%d,height=%d\n",width,height);
		
	pLogoInfo->height = height;
	pLogoInfo->stride = width;
	pLogoInfo->width = width;
	pLogoInfo->pBuffer = (unsigned char *)malloc(sizeof(char) * width*height*3/2);
	if (!pLogoInfo->pBuffer)
	{
		LOG_PrintError("Fail to malloc memory!\n");
		return -1;
	}
	memset(pLogoInfo->pBuffer, 0, sizeof(char) * width*height*3/2);

	yuvData = pLogoInfo->pBuffer;

	Uint32 len = sizeof(char) * width*height*3/2;

	ENC_rgba8888_ToYuv420Nv12(pData, yuvData, width, height, len);

	

	return 0;
}

#if 1
int ENC_Rgb8888ToYUV(BMP_logoInfo_t *pLogoInfo,BITMAP_S *pstBitmap)
{
	unsigned int * pData = pstBitmap->pData;
	unsigned char *yuvData;
	
	unsigned int width = pstBitmap->u32Width;
	unsigned int height = pstBitmap->u32Height;
	
	unsigned int a, r, g, b, nsize;
	unsigned char *y, *uv, *tmpu, *tmpv;

	int w, h, k;

	//FILE* fp;
	//fp = fopen("/tmp/nfs/1.yuv", "w+");
		
	pLogoInfo->height = height;
	pLogoInfo->stride = width;
	pLogoInfo->width = width;
	pLogoInfo->pBuffer = (unsigned char *)malloc(sizeof(char) * width*height*3/2);
	if (!pLogoInfo->pBuffer)
	{
		LOG_PrintError("Fail to malloc memory!\n");
		return -1;
	}
	memset(pLogoInfo->pBuffer, 0, sizeof(char) * width*height*3/2);

	yuvData = pLogoInfo->pBuffer;
		
	y = yuvData;
	uv = y + width*height;
	
    tmpu = (unsigned char *)malloc(width*height);
    if (!tmpu)
    {
        LOG_PrintError("Fail to malloc memory!\n");
        return -1;
    }
    tmpv = (unsigned char *)malloc(width*height);
    if (!tmpv)
    {
        LOG_PrintError("Fail to malloc memory!\n");
        free(tmpu);
        return -1;
    }

	for(nsize=0;nsize<width*height;nsize++)
	{
		a = (pData[nsize]&0xff000000)>>24;
		r = (pData[nsize]&0x00ff0000)>>16;
		g = (pData[nsize]&0x0000ff00)>>8;
		b = (pData[nsize]&0x000000ff);

		y[nsize] = (unsigned char)((float)(0.2990*r) + (float)(0.5870*g) + (float)(0.1140*b));
		if((a) && (y[nsize] == 0))//勾边的像素点
		{
			y[nsize] = 1;
		}
		tmpu[nsize] = (unsigned char)(-(float)(0.1684*r) - (float)(0.3316*g) + (float)(b*0.5) + 128);
		tmpv[nsize] = (unsigned char)((float)(r*0.5) - (float)(0.4187*g) - (float)(0.0813*b) + 128);
	}

	//LOG_PrintInfo("test ENC_Rgb8888ToYUV,height=%d,width=%d\n",height,width);

    for (h = 0, k = 0; h < height; h+=2) //height与width需要偶数，否则会越界
    {
        for(w = 0; w < width; w+=2)
        {
            uv[k]=(tmpu[h*width+w] + tmpu[(h+1)*width+w] 
                        + tmpu[h*width+w+1] + tmpu[(h+1)*width+w+1])/4;
			k++;
            uv[k]=(tmpv[h*width+w] + tmpv[(h+1)*width+w] 
                        + tmpv[h*width+w+1] + tmpv[(h+1)*width+w+1])/4;
            k++;
        }
    }
	
/*    for (nsize = 0; nsize < width*height; nsize++)
    {
        if(y[nsize] < 16)
        {
            y[nsize] = 16;
        }
        if(y[nsize] > 235)
        {
            y[nsize] = 235;
        }
    }
	
    for(nsize = 0; nsize < height*width/2; nsize++)
    {
        if(uv[nsize] < 16)
        {
            uv[nsize] = 16;
        }
        if(uv[nsize] > 240)
        {
            uv[nsize] = 240;
        }
        nsize++;
        
        if(uv[nsize] < 16)
        {
            uv[nsize] = 16;
        }
        if(uv[nsize] > 240)
        {
            uv[nsize] = 240;
        }
    }*/
	
    if(tmpu)
    {
        free(tmpu);
    }
    if(tmpv)
    {
        free(tmpv);
    }
	//printf("%d x %d\n", width,height);
	//fwrite(yuvData, width*height*3/2, 1, fp);
	//fclose(fp);

	return 0;
}

#endif


/*******************************************************************************
  Function:     ENC_StartRoi
  Description:  启动ROI
  Input:        - pEncCtl: 编码通道控制结构体 
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_StartRoi(ENC_ctl_t *pEncCtl)
{
    return 0;
}


/*******************************************************************************
  Function:     ENC_SetRoi
  Description:  设置编码感兴趣区域Roi
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetRoi(struct VideoEncoder *pThiz, VideoEncROI *pRoi) 
{
    return 0;
}

/*******************************************************************************
  Function:     ENC_SetWatermark
  Description:  设置水印
  Input:        - pThiz: 视频编码接口指针
                - pMark: 水印
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetWatermark(struct VideoEncoder *pThiz, VideoWaterMark *pMark)
{    
    return 0;
}


/*******************************************************************************
  Function:     ENC_ParseRgb1555
  Description:  颜色格式转换
  Input:        - pFgColor: rgb颜色 
  Output:       - pRgbBuf: rgb1555格式颜色值
  Return:       无
  Others:       无
*******************************************************************************/
static inline void ENC_ParseRgb1555(unsigned char *pRgbBuf, Color *pFgColor)
{
    unsigned int rgb = 0;
    unsigned int r, g, b, a;

    r = pFgColor->r;
    g = pFgColor->g;
    b = pFgColor->b;
    a = pFgColor->a;
    
    rgb = b >> 3;
    rgb = rgb |((g >> 3) << 5);
    rgb = rgb |((r >> 3) << 10);
    rgb = rgb |((a?0:1) << 15);
    pRgbBuf[0] = (rgb) & 0xFF;
    pRgbBuf[1] = ((rgb) & 0xFF00) >> 8;
}


/*******************************************************************************
  Function:     ENC_SetTitleUnlock
  Description:  叠加视频编码的标题参数。
  Input:        - pThiz: 视频编码接口指针
                - pTitle: title
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_UpdateTitle(ENC_ctl_t *pEncCtl)
{
    return 0;
}

/*******************************************************************************
  Function:     ENC_SetTitle
  Description:  叠加视频编码的标题参数。
  Input:        - pThiz: 视频编码接口指针
                - pTitle: title
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetTitle(struct VideoEncoder *pThiz, VideoTitle *pTitle)
{
    return 0;
}


/*******************************************************************************
  Function:     ENC_SetCover
  Description:  设置遮挡
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetCover(struct VideoEncoder *pThiz, VideoCover *pCover)
{
    int ret = 0;
    ENC_ctl_t *pEncCtl;
    
    if (!pThiz || !pCover)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    pEncCtl->isCover = pCover->enable;
    
mutex_unlock:
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}

/*******************************************************************************
  Function:     ENC_GetCfg
  Description:  获取编码配置参数
  Input:        - pEncCtl: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_GetCfg(ENC_ctl_t *pEncCtl)
{
    int fps;
	VIDEO_picSize_t picSize;
    VI_CHN_ATTR_S *pViChnAttr;
    VENC_CHN_ATTR_S *pVencAttr;
    RK_CHAR *pDeviceName = NULL;

    pViChnAttr = &pEncCtl->viChnAttr;
    pVencAttr = &pEncCtl->vencAttr;
    
    pEncCtl->fps = fps = (int)videoCtl_g.fps;
    pEncCtl->packType = videoEncPackTypeZLAV;               
    pEncCtl->isCreateVenc = 1;
    
    pEncCtl->depthNum = 0;
    pEncCtl->extDepthNum = 0;

    VIDEO_GetPicSize(videoCtl_g.standard, 
                             VIDEO_RESOLUTION_H1080, 
                             &picSize,
                             videoCtl_g.rotate);
    pEncCtl->encPicSize = picSize;  

    return 0;
}

/*******************************************************************************
  Function:     ENC_AddRef
  Description:  增加接口引用
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_AddRef(struct VideoEncoder *pThiz)
{
    ENC_ctl_t *pEncCtl;

    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }
    
    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    pEncCtl->refCount++;
    
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    
    return 0;
}

/*******************************************************************************
  Function:     ENC_Release
  Description:  释放接口
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_Release(struct VideoEncoder *pThiz)
{
    int ret = 0;
    ENC_ctl_t *pEncCtl;
    
    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }
    
    pthread_mutex_lock(&videoCtl_g.encodeMutex);
    
    if (pEncCtl->refCount > 1)
    {
        pEncCtl->refCount--;
        goto unlock_mutex;
    }
    
    videoCtl_g.pEncCtls[pEncCtl->streamId] = NULL;
    free(pEncCtl);
    
unlock_mutex:       
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}


void ENC_Nv12ToYuyv(int width, int height, void* src, void* dst)
{
    int i = 0, j = 0;
    int* src_y = (int*)src;
    int* src_uv = (int*)((char*)src + width * height);
    int* line = (int*)dst;

    for (j = 0; j < height; j++) {
        if (j % 2 != 0)
            src_uv -= width >> 2;
        for (i = 0; i<width >> 2; i++) {
            *line++ = ((*src_y & 0x000000ff)) | ((*src_y & 0x0000ff00) << 8) |
                      ((*src_uv & 0x000000ff) << 8) | ((*src_uv & 0x0000ff00) << 16);
            *line++ = ((*src_y & 0x00ff0000) >> 16) | ((*src_y & 0xff000000) >> 8) |
                      ((*src_uv & 0x00ff0000) >> 8) | ((*src_uv & 0xff000000));
            src_y++;
            src_uv++;
        }
    }
}

void ENC_Nv12ToYuyvStretch(int width, int height, void* src, void* dst,int iDstWid,int iDstHei)
{
    int i = 0, j = 0;
    int* src_y = (int*)src;
    int* src_uv = (int*)((char*)src + width * height);
    int iDiffWid = iDstWid - width;
    int iDiffHei = iDstHei - height;

    int iBlack = 0x80108010; /* black y:16,u:128,v:128 */

    int* line = (int*)dst;

    for (j = 0; j < iDiffHei/ 2; j++)
    {
        for (i = 0; i<width >> 2; i++)
        {
            *line++ = iBlack; 
            *line++ = iBlack;
        }
    }

    line = (int*)dst + iDiffHei * width  / 4;

    for (j = 0; j < height; j++) {
        if (j % 2 != 0)
            src_uv -= width >> 2;
        for (i = 0; i<width >> 2; i++) {
            *line++ = ((*src_y & 0x000000ff)) | ((*src_y & 0x0000ff00) << 8) |
                      ((*src_uv & 0x000000ff) << 8) | ((*src_uv & 0x0000ff00) << 16);
            *line++ = ((*src_y & 0x00ff0000) >> 16) | ((*src_y & 0xff000000) >> 8) |
                      ((*src_uv & 0x00ff0000) >> 8) | ((*src_uv & 0xff000000));
            src_y++;
            src_uv++;
        }
    }

    line = (int*)dst + (iDiffHei / 2 + height) * width / 2;

    for (j = 0; j < iDiffHei / 2; j++)
    {
        for (i = 0; i<width >> 2; i++)
        {
            *line++ = iBlack;
            *line++ = iBlack;
        }
    }

}

static int ENC_GetYuv422Stretch(ENC_ctl_t *pEncCtl, MEDIA_BUFFER mb,int iDstWid,int iDstHei)
{
    int bufLen;
    MB_IMAGE_INFO_S mbInfo;
    NEC_listBufInfo_t *pListFrame;
    
    bufLen = RK_MPI_MB_GetSize(mb);

    RK_MPI_MB_GetImageInfo(mb, &mbInfo);

    bufLen = 2*iDstWid*iDstHei;

    pthread_mutex_lock(&pEncCtl->bufLock);
    if(!list_empty(&pEncCtl->emptyBuf))
    {
        pListFrame = list_first_entry(&pEncCtl->emptyBuf, NEC_listBufInfo_t, list);
        list_del(&pListFrame->list);
        pthread_mutex_unlock(&pEncCtl->bufLock);

        pListFrame->isIframe = 0;
        pListFrame->len = bufLen;

        ENC_Nv12ToYuyvStretch(mbInfo.u32Width, mbInfo.u32Height,
                        RK_MPI_MB_GetPtr(mb), pListFrame->pBuf,iDstWid,iDstHei);

        pthread_mutex_lock(&pEncCtl->bufLock);
        list_add_tail(&pListFrame->list, &pEncCtl->fullBuf);
        pthread_mutex_unlock(&pEncCtl->bufLock);
    }
    else
    {
        pthread_mutex_unlock(&pEncCtl->bufLock);
        LOG_PrintWarn("emptyBuf list is enpty!\n");
    }

    return 0;
}


int ENC_GetYuv422(ENC_ctl_t *pEncCtl, MEDIA_BUFFER mb)
{
    int bufLen;
    MB_IMAGE_INFO_S mbInfo;
    NEC_listBufInfo_t *pListFrame;
    
    bufLen = RK_MPI_MB_GetSize(mb);

    RK_MPI_MB_GetImageInfo(mb, &mbInfo);

    if ((mbInfo.u32Width != pEncCtl->encPicSize.u32Width) 
        || (mbInfo.u32Height != pEncCtl->encPicSize.u32Height))
    {
        LOG_PrintWarn("farme width(%d) height(%d) is invalid.\n",
                        mbInfo.u32Width, mbInfo.u32Height);
        return -1;
    }

    if (bufLen > pEncCtl->maxLen)
    {
        LOG_PrintError("bufLen is invalid,(%d, %d)\n", bufLen, pEncCtl->maxLen);
        return -1;
    }

    if ((mbInfo.u32Width*mbInfo.u32Height*3/2) != bufLen)
    {
        LOG_PrintError("bufLen(%d) is invalid.\n", bufLen);
        return -1;
    }

    bufLen = 2*mbInfo.u32Width*mbInfo.u32Height;

    pthread_mutex_lock(&pEncCtl->bufLock);
    if(!list_empty(&pEncCtl->emptyBuf))
    {
        pListFrame = list_first_entry(&pEncCtl->emptyBuf, NEC_listBufInfo_t, list);
        list_del(&pListFrame->list);
        pthread_mutex_unlock(&pEncCtl->bufLock);

        pListFrame->isIframe = 0;
        pListFrame->len = bufLen;
        //memcpy(pListFrame->pBuf, RK_MPI_MB_GetPtr(mb), bufLen);

        ENC_Nv12ToYuyv(mbInfo.u32Width, mbInfo.u32Height,
                        RK_MPI_MB_GetPtr(mb), pListFrame->pBuf);

        pthread_mutex_lock(&pEncCtl->bufLock);
        list_add_tail(&pListFrame->list, &pEncCtl->fullBuf);
        pthread_mutex_unlock(&pEncCtl->bufLock);
    }
    else
    {
        pthread_mutex_unlock(&pEncCtl->bufLock);
        LOG_PrintWarn("emptyBuf list is enpty!\n");
    }

    return 0;
}

static __attribute__((noinline)) void neon_memcpy(volatile unsigned char *dst, volatile unsigned char *src, int sz)
{
    if (sz & 63)
        sz = (sz & -64) + 64;
    asm volatile(
    "NEONCopyPLD: \n"
            " PLD  [%[src], #0xC0] \n"
            " VLDM %[src]!,{d0-d7} \n"
            " VSTM %[dst]!,{d0-d7} \n"
            " SUBS %[sz],%[sz],#0x40 \n"
            " BGT NEONCopyPLD \n"
    : [dst]"+r"(dst), [src]"+r"(src), [sz]"+r"(sz) : : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "cc", "memory");
}

static void ENC_PacketCb(MEDIA_BUFFER mb) 
{
    const char *nalu_type = "Jpeg data";
    ENC_ctl_t *pEncCtl;
    NEC_listBufInfo_t *pListFrame;
    int isIframe = 0;
    int bufLen;
    MB_IMAGE_INFO_S mbInfo;
    unsigned short chnId;

    chnId = RK_MPI_MB_GetChannelID(mb);
    if (RK_MPI_MB_GetModeID(mb) == RK_ID_VENC) 
    {
        if (chnId >= (ENC_SUPPORT_CHN * ENC_STREAM_PER_CHN))
        {
            LOG_PrintError("chnId(%d) is invalid.\n", chnId);
            goto release_buf;
        }
        pEncCtl = videoCtl_g.pEncCtls[chnId];
        if (!pEncCtl)
        {
            LOG_PrintError("pEncCtl is NULL\n");
            goto release_buf;
        }
    
        switch (RK_MPI_MB_GetFlag(mb)) 
        {
            case VENC_NALU_IDRSLICE:
                nalu_type = "IDR Slice";
                isIframe = 1;
                break;
            case VENC_NALU_PSLICE:
                nalu_type = "P Slice";
                break;
            default:
                break;
        }

        bufLen = RK_MPI_MB_GetSize(mb);

        if (bufLen > pEncCtl->maxLen || bufLen <= 0)
        {
            LOG_PrintError("bufLen is invalid,(%d, %d)\n", bufLen, pEncCtl->maxLen);
            goto release_buf;
        }

        pthread_mutex_lock(&pEncCtl->bufLock);
        if(BYOM_OUT == chnId){
            if(pEncCtl->needLoseFrame && isIframe){
                pEncCtl->needLoseFrame = 0;
            }

            if(!list_empty(&pEncCtl->emptyBuf) && !pEncCtl->needLoseFrame){
                pListFrame = list_first_entry(&pEncCtl->emptyBuf, NEC_listBufInfo_t, list);
                list_del(&pListFrame->list);
                pthread_mutex_unlock(&pEncCtl->bufLock);

                pListFrame->isIframe = isIframe;
                pListFrame->len = bufLen;
                pListFrame->pts = RK_MPI_MB_GetTimestamp(mb);
                memcpy(pListFrame->pBuf, RK_MPI_MB_GetPtr(mb), bufLen);

                // printf("ENC_PacketCb: chnId %d pts=%llu, bufLen=%d\n",chnId, pListFrame->pts, bufLen);

                pthread_mutex_lock(&pEncCtl->bufLock);
                list_add_tail(&pListFrame->list, &pEncCtl->fullBuf);
                pthread_mutex_unlock(&pEncCtl->bufLock);
            }
            else{
                LOG_PrintWarn("lose video frame pts %llu\n", RK_MPI_MB_GetTimestamp(mb));
                pEncCtl->needLoseFrame = 1;
                pthread_mutex_unlock(&pEncCtl->bufLock);
            }
        }else{
            if(!list_empty(&pEncCtl->emptyBuf)){
                pListFrame = list_first_entry(&pEncCtl->emptyBuf, NEC_listBufInfo_t, list);
                list_del(&pListFrame->list);
                pthread_mutex_unlock(&pEncCtl->bufLock);

                pListFrame->isIframe = isIframe;
                pListFrame->len = bufLen;
                pListFrame->pts = RK_MPI_MB_GetTimestamp(mb);
                memcpy(pListFrame->pBuf, RK_MPI_MB_GetPtr(mb), bufLen);

                // printf("ENC_PacketCb: chnId %d pts=%llu, bufLen=%d\n",chnId, pListFrame->pts, bufLen);

                pthread_mutex_lock(&pEncCtl->bufLock);
                list_add_tail(&pListFrame->list, &pEncCtl->fullBuf);
                pthread_mutex_unlock(&pEncCtl->bufLock);
            }
            else{
                pthread_mutex_unlock(&pEncCtl->bufLock);
                LOG_PrintWarn("emptyBuf list is empty!\n");
            }
        }
    }

release_buf:
    RK_MPI_MB_ReleaseBuffer(mb);

    return ;
}

static int ENC_DeinitList(ENC_ctl_t *pEncCtl)
{
    NEC_listBufInfo_t *pListFrame;
    
    while (!list_empty(&pEncCtl->fullBuf))
    {
        pListFrame = list_first_entry(&pEncCtl->fullBuf, NEC_listBufInfo_t, list);
        list_del(&pListFrame->list);
        list_add_tail(&pListFrame->list, &pEncCtl->emptyBuf);
    }
            
    return 0;
}

static void ENC_ClearBufList(ENC_ctl_t *pEncCtl)
{
    NEC_listBufInfo_t *pListFrame;

    pthread_mutex_lock(&pEncCtl->bufLock);

    while(!list_empty(&pEncCtl->fullBuf))
    {
        /* full-list --> empty-list */
        pListFrame = list_first_entry(&pEncCtl->fullBuf, NEC_listBufInfo_t, list);
        list_del(&pListFrame->list);

        list_add_tail(&pListFrame->list, &pEncCtl->emptyBuf);
    }

    pthread_mutex_unlock(&pEncCtl->bufLock);

}

/*******************************************************************************
  Function:     ENC_StartUnlock
  Description:  启动编码,不加锁
  Input:        - pEncCtl: 编码通道控制结构体 
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_StartUnlock(ENC_ctl_t *pEncCtl)
{
    int ret;
    MPP_CHN_S stEncChn;
    MOD_ID_E enModId;

    LOG_PrintWarn("pEncCtl->vencChn:%d\n", pEncCtl->vencChn);
    
    if (pEncCtl->encState != ENC_STOP)
    {
    	LOG_PrintError("venc state is not stop!\n");
        return -1;
    } 

    if (pEncCtl->isCreateVenc)
    {
        ret = RK_MPI_VENC_CreateChn(pEncCtl->vencChn, &pEncCtl->vencAttr);
        if (ret)
        {
            LOG_PrintError("Fail to RK_MPI_VENC_CreateChn chn(%d) with ret=%d\n",
                            pEncCtl->vencChn, ret);
            return -1;
        }
        enModId = RK_ID_VENC;

#ifdef CALL_BACK
        stEncChn.enModId = enModId;
        stEncChn.s32DevId = 0;
        stEncChn.s32ChnId = pEncCtl->vencChn;
        ret = RK_MPI_SYS_RegisterOutCb(&stEncChn, ENC_PacketCb);
        if (ret) 
        {
            LOG_PrintError("Fail to RK_MPI_SYS_RegisterOutCbret=%d\n", ret);
            return -1;
        }
#endif

        ret = RK_MPI_VENC_RGN_Init(pEncCtl->vencChn, NULL);
    	if (ret)
    	{
    		LOG_PrintError("RK_MPI_VENC_RGN_Init err:%d\n", ret);
    		return -1;
    	}

        /*if (pEncCtl->encType == videoEncMotionJPEG)
        {
    	    VENC_JPEG_PARAM_S stJpegParam;

    	    memset(&stJpegParam, 0, sizeof(stJpegParam));

    	    stJpegParam.u32Qfactor = 95;
            ret = RK_MPI_VENC_SetJpegParam(pEncCtl->vencChn, &stJpegParam);
            if (ret)
        	{
        		LOG_PrintError("RK_MPI_VENC_SetJpegParam err:%d\n", ret);
        		return -1;
        	}
    	}*/
    }
    
    pEncCtl->iFrameCnt = 0;

    /* clear buffer */
    ENC_ClearBufList(pEncCtl);

    pEncCtl->encState = ENC_START_EMPTY;

    return 0;
}

/*******************************************************************************
  Function:     ENC_StopUnlock
  Description:  停止编码,不加锁
  Input:        - pEncCtl: 编码通道控制结构体 
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_StopUnlock(ENC_ctl_t *pEncCtl)
{
    int result;

    LOG_PrintWarn("pEncCtl->vencChn:%d\n", pEncCtl->vencChn);

	if (pEncCtl->encState == ENC_STOP)
	{
		LOG_PrintWarn("venc chn %d have been stoped!\n", pEncCtl->vencChn);
		return 0;
	}

    pEncCtl->encState = ENC_STOP;

    if (pEncCtl->isCreateVenc)
    {
        result = RK_MPI_VENC_DestroyChn(pEncCtl->vencChn);
        if (result) 
        {
            LOG_PrintError("Fail to RK_MPI_VENC_DestroyChn chn(%d) with ret=%d\n",
                            pEncCtl->vencChn, result);
            return -1;
        }
    }

#ifdef CALL_BACK
    ENC_DeinitList(pEncCtl);
#endif

    return 0;
}


/*******************************************************************************
  Function:     ENC_Start
  Description:  启动编码
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_Start(struct VideoEncoder *pThiz)
{
    int ret = 0, result;
    ENC_ctl_t *pEncCtl;
    
    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    result = ENC_StartUnlock(pEncCtl);
    if (result)
    {
        LOG_PrintError("Fail to start encode channel %d!\n", pEncCtl->vencChn);
        ret = -1;
        goto mutex_unlock;
    }

mutex_unlock:
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}

/*******************************************************************************
  Function:     ENC_Stop
  Description:  停止编码
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_Stop(struct VideoEncoder *pThiz)
{
    int ret = 0, result;
    ENC_ctl_t *pEncCtl;
    
    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }
    
    pthread_mutex_lock(&videoCtl_g.encodeMutex);
    
    result = ENC_StopUnlock(pEncCtl);
    if (result)
    {
        LOG_PrintError("Fail to stop encode channel %d!\n", pEncCtl->vencChn);
        ret = -1;
        goto mutex_unlock;
    }

mutex_unlock:
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    usleep(33*1000);
    return ret;
}


/*******************************************************************************
  Function:     ENC_GetChipId
  Description:  得到编码器所在的芯片ID
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       >=0 芯片ID，由库里面统一分配，不同的芯片有唯一的编号。
                    芯片号在创建编码通道的时候得到
                <0: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_GetChipId(struct VideoEncoder *pThiz)
{
    return videoCtl_g.cpuType;
}

/*******************************************************************************
  Function:     ENC_GetChipCaps
  Description:  获得芯片的编码能力
  Input:        - pThiz: 视频编码接口指针
  Output:       - pEncChipCaps: 芯片能力结构VideoEncChipCaps指针
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_GetChipCaps(struct VideoEncoder *pThiz, 
                           VideoEncChipCaps *pEncChipCaps)
{
    ENC_ctl_t *pEncCtl;
    
    if ((NULL == pThiz) || (NULL == pEncChipCaps))
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    switch (videoCtl_g.cpuType)
    {            
        case SYS_CPU_HI3559AV100:
            pEncChipCaps->ladenBitrate
                = (7680*4320*30+1920*1080*30)/64;
            break;   
        case SYS_CPU_HI3519AV100:
            pEncChipCaps->ladenBitrate
                = (3840*2160*60+1280*720*30)/64;
            break;    
		case SYS_CPU_HI3516CV500:
		case SYS_CPU_HI3516DV300:
            pEncChipCaps->ladenBitrate
                = (1920*1080*30+720*480*30+360*240*30)/64;
            break;   
		case SYS_CPU_HI3516EV200:
		case SYS_CPU_HI3518EV300:
			pEncChipCaps->ladenBitrate
				= (2048*1536*20+720*576*20)/64;
			break; 
		case SYS_CPU_HI3516EV300:
			pEncChipCaps->ladenBitrate
				= (2048*1536*30+720*576*30)/64;
			break;      
        default:
            LOG_PrintError("cpu type %d is invalid!\n", videoCtl_g.cpuType);
            return -1;
    }
    
    return 0;
}

/*******************************************************************************
  Function:     ENC_GetCaps
  Description:  得到视频编码设备能力
  Input:        - pThiz: 视频编码接口指针
  Output:       - pEncCaps: 编码能力结构VideoEncCaps指针
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_GetCaps(struct VideoEncoder *pThiz, VideoEncCaps *pEncCaps)
{
    ENC_ctl_t *pEncCtl;
    
    if ((NULL == pThiz) || (NULL == pEncCaps))
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pEncCaps->dynamicEnalbe = 1;

    switch (videoCtl_g.cpuType)
    {             
		case SYS_CPU_HI3559AV100:
		case SYS_CPU_HI3519AV100:
		case SYS_CPU_HI3516CV500:
		case SYS_CPU_HI3516EV300:
		case SYS_CPU_HI3518EV300:
		case SYS_CPU_HI3516DV300:
            pEncCaps->types = (1 << videoEncH264) 
                              | (1 << videoEncH264_Improve)
                              | (1 << videoEncH265)
                              | (1 << videoEncH265_Improve)
                              | (1 << videoEncMotionJPEG);
            pEncCaps->coverCount = ENC_COVER_NUM;
            pEncCaps->titleCount = ENC_TITLE_NUM;
            pEncCaps->snapByOtherSize = 1;
            pEncCaps->coverRemain = 0;
            pEncCaps->snapFps = (VIDEO_STANDARD_PAL == videoCtl_g.standard) ? 25 : 30;
            pEncCaps->roiCount = MAX_ROI_NUM;
            pEncCaps->maxGop = 150;
            pEncCaps->roiBackgroundFpsEnable = 1;
            pEncCaps->h264ProfileMask = (1 << h264ProfileBaseline) 
                                        | (1 << h264ProfileMain)
                                        | (1 << h264ProfileHigh);
            pEncCaps->svcTypeMask = (1 << videoSvcTime);
            pEncCaps->snapScaleEnalbe = 1;
            pEncCaps->refModeMask = videoNomalRefMode | videoAdvanceRefMode;
            break;
        case SYS_CPU_HI3516EV200:
            pEncCaps->types = (1 << videoEncH264) | (1 << videoEncH265);

            if (videoCtl_g.maxResolution == VIDEO_RESOLUTION_H1080)
            {
                pEncCaps->types |= (1 << videoEncH264_Improve)
                                   | (1 << videoEncH265_Improve);
            }
            
            pEncCaps->coverCount = ENC_COVER_NUM;
            pEncCaps->titleCount = ENC_TITLE_NUM;
            pEncCaps->snapByOtherSize = 1;
            pEncCaps->coverRemain = 0;
            pEncCaps->snapFps = (VIDEO_STANDARD_PAL == videoCtl_g.standard) ? 25 : 30;
            pEncCaps->roiCount = MAX_ROI_NUM;
            pEncCaps->maxGop = 150;
            pEncCaps->roiBackgroundFpsEnable = 1;
            pEncCaps->h264ProfileMask = (1 << h264ProfileBaseline) 
                                        | (1 << h264ProfileMain)
                                        | (1 << h264ProfileHigh);
            pEncCaps->svcTypeMask = (1 << videoSvcTime);
            pEncCaps->snapScaleEnalbe = 1;
            pEncCaps->refModeMask = 0;
			break;         
        default:
            LOG_PrintError("cpu type %d is invalid!\n", videoCtl_g.cpuType);
            return -1;
    }
    
    return 0;
}


/*******************************************************************************
  Function:     ENC_ForceIFrame
  Description:  强制编码器产生I帧。
                新的I帧应该在下一个数据包VideoEncPacket第一帧。
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_ForceIFrame(struct VideoEncoder *pThiz)
{
    int ret = 0;
    RK_S32 s32Ret;
    RK_BOOL bInstant = 1;
    ENC_ctl_t *pEncCtl;
    
    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    if(pEncCtl->encStream >= videoEncStreamSnapshot)
    {
        return 0; 
    }

    if((pEncCtl->encType == videoEncMotionJPEG)
        || (pEncCtl->encType == videoYuv420))
    { 
        return 0; 
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    if (pEncCtl->encState == ENC_STOP)
    {
        LOG_PrintWarn("Venc channel %d is stopped!\n", pEncCtl->vencChn);
        ret = -1;
        goto mutex_unlock;
    }

    s32Ret = RK_MPI_VENC_RequestIDR(pEncCtl->vencChn, bInstant);
    if (RK_SUCCESS != s32Ret)
    {
        LOG_PrintError("RK_MPI_VENC_RequestIDR (%d) failed %d!\n",  
                        pEncCtl->vencChn, s32Ret);              
        ret = -1;
        goto mutex_unlock;
    }

mutex_unlock:  
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}



/*******************************************************************************
  Function:     ENC_QualityLevel
  Description:  上层和海思编码库之间编码QP值定义转换
  Input:        - quality: 应用层编码QP值定义
  Output:       无
  Return:       图像质量等级
  Others:       无
*******************************************************************************/
static inline unsigned int ENC_QualityLevel(unsigned int quality)
{
    if((quality >= 1) && (quality <= 16))
    {
        return 0;
    }

    if((quality >= 17) && (quality <= 32))
    {
        return 1;
    }

    if((quality >= 33) && (quality <= 48))
    {
        return 2;
    }

    if((quality >= 49) && (quality <= 64))
    {
        return 3;
    }

    if((quality >= 65) && (quality <= 80))
    {
        return 4;
    }

    if((quality >= 81) && (quality <= 100))
    {
        return 5;
    }

    return  0;
}



/*******************************************************************************
  Function:     ENC_PrintParams
  Description:  视频编码通道配置参数
  Input:        - pEncCtl: 视频编码通道控制结构本指针
  Output:       无
  Return:       无
  Others:       无
*******************************************************************************/
static void ENC_PrintParams(ENC_ctl_t *pEncCtl)
{
    return ;
}



/*******************************************************************************
  Function:     ENC_SetFormat
  Description:  设置视频编码数据压缩的格式
  Input:        - pThiz: 视频编码接口指针
                - pFormat 指向视频格式结构VideoEncFormat的指针，
                          NULL表示不编码视频。
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetFormat(struct VideoEncoder *pThiz, VideoEncFormat *pFormat)
{
    int ret = 0;
    ENC_ctl_t *pEncCtl;
    VENC_CHN_ATTR_S vencAttr;
    unsigned int viFrameRate;
    VIDEO_picSize_t picSize; 
    int statTime = 2;
    
    if (!pThiz || !pFormat)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    if (pEncCtl->encState != ENC_STOP)
    {
        LOG_PrintError("venc channel %d is running!\n", pEncCtl->vencChn);
        ret = -1;
        goto mutex_unlock;
    }

    LOG_PrintInfo("vencChn:%d, bitrate %d, bitrateControl %d, fps %f, gop %d, "
                  "pack %d, profile %d height %d width %d type %d!\n",
                   pEncCtl->vencChn,
                   pFormat->bitrate, pFormat->bitrateControl, 
                   pFormat->fps, pFormat->gop, pFormat->pack,
                   pFormat->profile, pFormat->height, pFormat->width, pFormat->type);

    memset(&vencAttr, 0, sizeof(VENC_CHN_ATTR_S));
    
    if (pFormat->fps > videoCtl_g.fps)
    {
        pFormat->fps = videoCtl_g.fps;
    }

    VIDEO_GetPicSize(videoCtl_g.standard,
                     videoCtl_g.resolution,
                     &picSize,
                     videoCtl_g.rotate);

    //LOG_PrintInfo("picSize.u32Height=%d, picSize.u32Width=%d\n",picSize.u32Height, picSize.u32Width);
    if (pFormat->height > picSize.u32Height)
    {
        pFormat->height = picSize.u32Height;
    }

    if (pFormat->width > picSize.u32Width)
    {
        pFormat->width = picSize.u32Width;
    }

    if (pFormat->type == videoEncH264_Improve || 
    	pFormat->type == videoEncH265_Improve)
    {
        if (videoCtl_g.cpuType == SYS_CPU_HI3516EV200)
        {
            pEncCtl->refOptions.mode = videoNoRefMode;
        }
        else
        {
            pEncCtl->refOptions.mode = videoNomalRefMode;
            pEncCtl->refOptions.options.nomalModeOpt.refFrame = 4;
        }
        
        pEncCtl->enableRefIdr = 1;
        statTime = VENC_STATTIME;
        pEncCtl->gop = (unsigned int)pFormat->fps*2;
    }
    else
    {
        if (pFormat->refOptions)
        {
            memcpy(&pEncCtl->refOptions, 
                    pFormat->refOptions,
                    sizeof(VideoEncRefOptions));
        }
        else
        {
            pEncCtl->refOptions.mode = videoNoRefMode;
        }

        pEncCtl->gop = (unsigned int)pFormat->gop;
        pEncCtl->enableRefIdr = 0;
    }

#if 0
    if (pFormat->width >= 3840)
    {
        viFrameRate = 25;
    }
    else
    {
        viFrameRate = (unsigned int)videoCtl_g.fps;
    }
#else
    viFrameRate = (unsigned int)videoCtl_g.fps; //pFormat->fps;
#endif

    pEncCtl->fps = viFrameRate;

    pEncCtl->isCreateVenc = 1;

    if (pFormat->width == picSize.u32Width)
    {
        pEncCtl->isResize = 0;
    }
    else
    {
        pEncCtl->isResize = 1;
    }

    switch (pFormat->type)
    {
        case videoEncH264:
        case videoEncH264_Improve:
            vencAttr.stVencAttr.enType = RK_CODEC_TYPE_H264;
            vencAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
            vencAttr.stRcAttr.stH264Cbr.u32Gop = viFrameRate;

            vencAttr.stRcAttr.stH264Cbr.u32BitRate = pFormat->bitrate * 1024;
            
			if (vencAttr.stRcAttr.stH264Cbr.u32BitRate > 80000000)
            {
                vencAttr.stRcAttr.stH264Cbr.u32BitRate = 80000000;
            }
            // frame rate: in 30/1, out 30/1.
            vencAttr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
            vencAttr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = viFrameRate;
            vencAttr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
            vencAttr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = viFrameRate;
            break;
            
        case videoEncH265:
        case videoEncH265_Improve:
            vencAttr.stVencAttr.enType = RK_CODEC_TYPE_H265;
		    vencAttr.stRcAttr.enRcMode = VENC_RC_MODE_H265CBR;
		    vencAttr.stRcAttr.stH265Cbr.u32Gop = viFrameRate;
		    vencAttr.stRcAttr.stH265Cbr.u32BitRate = pFormat->bitrate * 1024;
			if (vencAttr.stRcAttr.stH265Cbr.u32BitRate > 80000000)
            {
                vencAttr.stRcAttr.stH265Cbr.u32BitRate = 80000000;
            }
		    // frame rate: in 30/1, out 30/1.
		    vencAttr.stRcAttr.stH265Cbr.fr32DstFrameRateDen = 1;
		    vencAttr.stRcAttr.stH265Cbr.fr32DstFrameRateNum = viFrameRate;
		    vencAttr.stRcAttr.stH265Cbr.u32SrcFrameRateDen = 1;
		    vencAttr.stRcAttr.stH265Cbr.u32SrcFrameRateNum = viFrameRate;
            break;

        case videoEncMotionJPEG:        
            vencAttr.stVencAttr.enType = RK_CODEC_TYPE_MJPEG;
            vencAttr.stRcAttr.enRcMode = VENC_RC_MODE_MJPEGCBR;
            vencAttr.stRcAttr.stMjpegCbr.fr32DstFrameRateDen = 1;
            vencAttr.stRcAttr.stMjpegCbr.fr32DstFrameRateNum = viFrameRate;
            vencAttr.stRcAttr.stMjpegCbr.u32SrcFrameRateDen = 1;
            vencAttr.stRcAttr.stMjpegCbr.u32SrcFrameRateNum = viFrameRate;
            vencAttr.stRcAttr.stMjpegCbr.u32BitRate = pFormat->bitrate * 1024;
            if (vencAttr.stRcAttr.stMjpegCbr.u32BitRate > 70000000)
            {
                vencAttr.stRcAttr.stMjpegCbr.u32BitRate = 70000000;
            }
            /*vencAttr.stVencAttr.stAttrMjpege.u32ZoomWidth = pFormat->width;
            vencAttr.stVencAttr.stAttrMjpege.u32ZoomHeight = pFormat->height;
            vencAttr.stVencAttr.stAttrMjpege.u32ZoomVirWidth = pFormat->width;
            vencAttr.stVencAttr.stAttrMjpege.u32ZoomVirHeight = pFormat->height;*/
            break;
            
        case videoYuv420: 
            pEncCtl->isCreateVenc = 0;
            break;
        default:
            LOG_PrintError("stream type %d is unknow!\n", pFormat->type);
            ret =  -1;
            goto mutex_unlock;
    }

    /*VIDEO_GetPicSize(videoCtl_g.standard, 
                             videoCtl_g.resolution, 
                             &picSize,
                             videoCtl_g.rotate);*/

    vencAttr.stVencAttr.imageType = IMAGE_TYPE_NV12;
    vencAttr.stVencAttr.u32PicWidth = pFormat->width;
    vencAttr.stVencAttr.u32PicHeight = pFormat->height;
    vencAttr.stVencAttr.u32VirWidth = pFormat->width;
    vencAttr.stVencAttr.u32VirHeight = pFormat->height;
    vencAttr.stVencAttr.u32Profile = 77;

    memcpy(&pEncCtl->vencAttr, &vencAttr, sizeof(VENC_CHN_ATTR_S));
    
    pEncCtl->encPicSize.u32Width= pFormat->width;
    pEncCtl->encPicSize.u32Height = pFormat->height;
    pEncCtl->viChnAttr.u32Width = pFormat->width;
    pEncCtl->viChnAttr.u32Height = pFormat->height;
    //pEncCtl->fps = pFormat->fps;
    pEncCtl->packType = pFormat->pack;
    pEncCtl->encType = pFormat->type;
    
    ENC_PrintParams(pEncCtl);

mutex_unlock:  
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}

/*******************************************************************************
  Function:     ENC_RequestPacket
  Description:  查询是否有效数据包
                如果帧数据已经分块，则分多次传出。如果没有数据，则返回错误。
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       >0: 有效数据包长度
                 0: 无有效数据包
                <0: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_RequestPacket(struct VideoEncoder *pThiz)
{
    return 0;
}

/*******************************************************************************
  Function:     ENC_RecordData
  Description:  底层录像
  Input:        - pEncCtl: 编码通道控制结构体指针
                - pRawData:裸数据缓存
                - RawLen:裸数据长度
  Output:       - pEncPacket: 编码数据帧
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_RecordData(ENC_ctl_t *pEncCtl, VideoEncPacket *pEncPacket,
                            char *pRawData, int RawLen, int finishFlag)
{
	char fileName[64] = {0};
	int i;		     

    if (pEncCtl->recordData.recordFd < 0)
    {
        if (pEncCtl->encType == videoEncH264)
    	{
            sprintf(fileName, "%s%s_%ld.h264", pEncCtl->recordData.recordPath, 
            pEncCtl->recordData.recordName, time(NULL));
        }
        else if (pEncCtl->encType == videoEncH265)
        {
            sprintf(fileName, "%s%s_%ld.h265", pEncCtl->recordData.recordPath, 
            pEncCtl->recordData.recordName, time(NULL));
        }
    	else if (pEncCtl->encType == videoEncMotionJPEG)
    	{
            sprintf(fileName, "%s%s_%ld.jpg", pEncCtl->recordData.recordPath, 
            pEncCtl->recordData.recordName, time(NULL));
        }
   
        pEncCtl->recordData.recordFd = 
                        open(fileName, O_RDWR | O_CREAT | O_TRUNC, 0644);
    }

    if (1 == pEncCtl->recordData.isPackData)
    {
    	for (i = 0; i < pEncPacket->vSize; i++)
    	{
    	    write(pEncCtl->recordData.recordFd, pEncPacket->vBuffer[i].buffer, 
    	            pEncPacket->vBuffer[i].len);
    	}
	}
	else
	{
	    write(pEncCtl->recordData.recordFd, pRawData, RawLen);
	}

	if (pEncCtl->encType == videoEncMotionJPEG)
    {
        if (finishFlag == 1)
        {
            fsync(pEncCtl->recordData.recordFd);
            close(pEncCtl->recordData.recordFd);
            pEncCtl->recordData.recordFd = -1;
        }
    }

	return 0;
}


/*******************************************************************************
  Function:     ENC_GetPacket
  Description:  读取编码数据，读为非阻塞模式；每次最多读取一帧数据，
                如果帧数据已经分块，则分多次传出。如果没有数据，则返回错误。
  Input:        - pThiz: 视频编码接口指针
  Output:       - pEncPacket: 编码数据帧
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_GetPacket(struct VideoEncoder *pThiz, VideoEncPacket *pEncPacket)
{
    int ret=0;
    ENC_ctl_t *pEncCtl;

    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

	pthread_mutex_lock(&videoCtl_g.encodeMutex);

    if (pEncCtl->encState == ENC_STOP)
    {
        //LOG_PrintError("venc chn %d is not full!\n", pEncCtl->vencChn);
        ret = -1;
        goto mutex_unlock;
    }

#ifdef CALL_BACK
    NEC_listBufInfo_t *pListFrame;

    pthread_mutex_lock(&pEncCtl->bufLock);
    if(!list_empty(&pEncCtl->fullBuf))
    {
        pEncCtl->pListFrame = NULL;
        pListFrame = list_first_entry(&pEncCtl->fullBuf, NEC_listBufInfo_t, list);
        list_del(&pListFrame->list);
        pthread_mutex_unlock(&pEncCtl->bufLock);

        pEncCtl->pListFrame = pListFrame;
        pEncPacket->vBuffer[0].len = pListFrame->len;
        pEncPacket->vBuffer[0].buffer = pListFrame->pBuf;

        //memcpy(pEncPacket->vBuffer[0].buffer, pListFrame->pBuf, pListFrame->len);

        if (pListFrame->isIframe)
        {
            pEncPacket->type = videoEncFrameI;
        }
        else
        {
            pEncPacket->type = videoEncFrameP;
        }

        // printf("ENC_GetPacket:: pts=%llu, bufLen=%d\n",pListFrame->pts, pListFrame->len);
        pEncPacket->pts = pListFrame->pts;
        pEncPacket->encType = pEncCtl->encType;

#if 0
        pthread_mutex_lock(&pEncCtl->bufLock);
        list_add_tail(&pListFrame->list, &pEncCtl->emptyBuf);
        pthread_mutex_unlock(&pEncCtl->bufLock);
#endif
    }
    else
    {
        pthread_mutex_unlock(&pEncCtl->bufLock);
        ret = -1;
        //LOG_PrintWarn("fullBuf list is empty!\n");
        goto mutex_unlock;
    }
#else
    MEDIA_BUFFER buffer;

    pEncCtl->pBuffer = NULL;
    buffer = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, pEncCtl->vencChn, 0);
    if (buffer) 
    {
        pEncCtl->pBuffer = buffer ;
        pEncPacket->vBuffer[0].len = RK_MPI_MB_GetSize(buffer);
        pEncPacket->vBuffer[0].buffer = (uchar *)RK_MPI_MB_GetPtr(buffer);
        //memcpy(pEncPacket->vBuffer[0].buffer, RK_MPI_MB_GetPtr(buffer), pEncPacket->vBuffer[0].len);
        //RK_MPI_MB_ReleaseBuffer(buffer);
    }
    else
    {
        ret = -1;
        goto mutex_unlock;
    }
#endif

	if ((1 == pEncCtl->recordData.recordFlag) 
        && (1 == pEncCtl->recordData.isPackData)
        && (pEncPacket->type != videoEncFrameW))
    {
        ENC_RecordData(pEncCtl, pEncPacket, NULL, 0, 1);

        pEncCtl->recordData.frameNum--;
        if (0 == pEncCtl->recordData.frameNum)
        {
            pEncCtl->recordData.recordFlag = 0;

            if (pEncCtl->recordData.recordFd > 0)
            {
                fsync(pEncCtl->recordData.recordFd);
                close(pEncCtl->recordData.recordFd);
                pEncCtl->recordData.recordFd = -1;
            }
        }
    }
	
mutex_unlock:  
	pthread_mutex_unlock(&videoCtl_g.encodeMutex);
	return ret;
}

/*******************************************************************************
  Function:     ENC_GetPacket
  Description:  释放视频编码数据，需要和VideoEncGetPacket成对调用。
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_ReleasePacket(struct VideoEncoder *pThiz)
{
    int ret = 0, result;
    ENC_ctl_t *pEncCtl;

    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

#ifdef CALL_BACK
    pthread_mutex_lock(&pEncCtl->bufLock);
    if (pEncCtl->pListFrame)
    {
        list_add_tail(&pEncCtl->pListFrame->list, &pEncCtl->emptyBuf);
    }
    pthread_mutex_unlock(&pEncCtl->bufLock);
    pEncCtl->pListFrame = NULL;
#else
    if (pEncCtl->pBuffer)
    {
        RK_MPI_MB_ReleaseBuffer(pEncCtl->pBuffer);
        pEncCtl->pBuffer = NULL;
    }
#endif

mutex_unlock:  
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}


/*******************************************************************************
  Function:     ENC_GetCrop
  Description:  获取视频裁剪信息
  Input:        - pThiz: 视频编码接口指针
  Output:       - pCropCfg: 编码裁剪信息
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_GetCrop(struct VideoEncoder *pThiz, VideoEncCropCfg* pCropCfg)
{
    int ret = 0, result;
    ENC_ctl_t *pEncCtl;
    VIDEO_picSize_t picSize, maxSize;
    
    if (!pThiz || !pCropCfg)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    pCropCfg->enable = pEncCtl->isCrop;
    if (pCropCfg->enable)
    {
        pCropCfg->rect.x = pEncCtl->imOrgRect.x;
        pCropCfg->rect.y = pEncCtl->imOrgRect.y;
        pCropCfg->rect.w = pEncCtl->imOrgRect.width;
        pCropCfg->rect.h = pEncCtl->imOrgRect.height;
    }
    else
    {
        VIDEO_GetPicSize(videoCtl_g.standard, 
                         videoCtl_g.resolution, 
                         &picSize,
                         videoCtl_g.rotate);

        VIDEO_GetPicSize(videoCtl_g.standard, 
                         videoCtl_g.resolution, 
                         &maxSize,
                         videoCtl_g.rotate);
                         
        if ((maxSize.u32Width > picSize.u32Width)
            || (maxSize.u32Height > picSize.u32Height))
        {
            LOG_PrintError("width(%d), height(%d) is invalid.\n", picSize.u32Width, picSize.u32Height);
            ret = -1;
            goto mutex_unlock;
        }
        
        pCropCfg->rect.x = (picSize.u32Width - maxSize.u32Width)/2;
        pCropCfg->rect.y = (picSize.u32Height - maxSize.u32Height)/2;
        pCropCfg->rect.w = maxSize.u32Width;
        pCropCfg->rect.h = maxSize.u32Height;
    }
    
mutex_unlock:
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}



/*******************************************************************************
  Function:     ENC_SetCrop
  Description:  设置视频裁剪
  Input:        - pThiz: 视频编码接口指针
                - pCropCfg：视频裁剪配置
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetStitch(struct VideoEncoder *pThiz, VideoEncStitchCfg* pStitchCfg)
{
    int i;
    int ret = 0, result;
    ENC_ctl_t *pEncCtl;
    
    if (!pThiz || !pStitchCfg)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    if (pStitchCfg->onlyRefreshSpeak)
    {
        pEncCtl->stitchCfg.allRect.type = pStitchCfg->allRect.type;
        for (i = 0; i < pEncCtl->stitchCfg.num; i++)
        {
            pEncCtl->stitchCfg.rectInfo[i].type = pStitchCfg->rectInfo[i].type;
        }
    }
    else
    {
        memcpy(&pEncCtl->stitchCfg, pStitchCfg, sizeof(VideoEncStitchCfg));
    }

    pthread_mutex_unlock(&videoCtl_g.encodeMutex);

    return 0;
}

/*******************************************************************************
  Function:     ENC_SetScreenMode
  Description:  设置窗口模式
  Input:        - pThiz: 视频编码接口指针
                - mode：0：单画面；1、多画面拼接
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetScreenMode(struct VideoEncoder *pThiz, int mode)
{
    ENC_ctl_t *pEncCtl;
    
    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    if (pEncCtl->screenMode == mode)
    {
        goto mutex_unlock;
    }
    pEncCtl->screenMode = mode;

mutex_unlock:
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    
    return 0;
}

/*******************************************************************************
  Function:     ENC_SetDynamicFps
  Description:  设置动态帧率
  Input:        - pThiz: 视频编码接口指针
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetDynamicFps(struct VideoEncoder *pThiz, uint8 fps)
{
    int ret = 0;
    RK_S32 s32Ret;
    RK_BOOL bInstant = 1;
    ENC_ctl_t *pEncCtl;
    
    if (!pThiz)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    if(pEncCtl->encStream >= videoEncStreamSnapshot)
    {
        return 0; 
    }

    if (pEncCtl->encType == videoYuv420)
    { 
        return 0; 
    }

    if (fps >= pEncCtl->fps)
    { 
        return 0; 
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    if (pEncCtl->encState == ENC_STOP)
    {
        LOG_PrintWarn("Venc channel %d is stopped!\n", pEncCtl->vencChn);
        ret = -1;
        goto mutex_unlock;
    }

    s32Ret = RK_MPI_VENC_SetFps(pEncCtl->vencChn, fps, 1, pEncCtl->fps, 1);
    if (RK_SUCCESS != s32Ret)
    {
        LOG_PrintError("RK_MPI_VENC_SetFps (%d) failed %d!\n",  
                        pEncCtl->vencChn, s32Ret);              
        ret = -1;
        goto mutex_unlock;
    }
	
mutex_unlock:  
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}

/*******************************************************************************
  Function:     ENC_SetCrop
  Description:  设置视频裁剪
  Input:        - pThiz: 视频编码接口指针
                - pCropCfg：视频裁剪配置
  Output:       无
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_SetCrop(struct VideoEncoder *pThiz, VideoEncCropCfg* pCropCfg)
{
    int ret = 0, result;
    ENC_ctl_t *pEncCtl;
    VIDEO_picSize_t picSize;
    
    if (!pThiz || !pCropCfg)
    {
        LOG_PrintError("Input parameter is NULL\n");
        return -1;
    }

    pEncCtl = (ENC_ctl_t *)pThiz->priv;
    if (!pEncCtl)
    {
        LOG_PrintError("VideoEncoder isn't initialized\n");
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    if (pEncCtl->screenMode == 1)
    {
        goto mutex_unlock;
    }

    pEncCtl->isCrop = pCropCfg->enable;
    pEncCtl->imRect.x = VIDEO_ALIGN_DOWN(pCropCfg->rect.x, 2);
    pEncCtl->imRect.y = VIDEO_ALIGN_DOWN(pCropCfg->rect.y, 2);
    pEncCtl->imRect.width = VIDEO_ALIGN_DOWN(pCropCfg->rect.w, 4);
    pEncCtl->imRect.height = VIDEO_ALIGN_DOWN(pCropCfg->rect.h, 4);

    memcpy(&pEncCtl->imOrgRect, &pEncCtl->imRect, sizeof(im_rect));

    VIDEO_GetPicSize(videoCtl_g.standard, 
                     videoCtl_g.resolution, 
                     &picSize,
                     videoCtl_g.rotate);

    if (pEncCtl->imRect.x + pEncCtl->imRect.width > picSize.u32Width)
    {
        pEncCtl->imRect.width = VIDEO_ALIGN_DOWN(picSize.u32Width - pEncCtl->imRect.x, 4);
    }
    
    if (pEncCtl->imRect.y + pEncCtl->imRect.height > picSize.u32Height)
    {
        pEncCtl->imRect.height = VIDEO_ALIGN_DOWN(picSize.u32Height - pEncCtl->imRect.y, 4);
    }

    if ((pEncCtl->imRect.width == picSize.u32Width) 
        && (pEncCtl->imRect.height == picSize.u32Height))
    {
        pEncCtl->isCrop = 0;
    }

    /*LOG_PrintWarn("isCrop:%d, x:%d, y:%d, w:%d, h:%d\n", 
    pEncCtl->isCrop, pEncCtl->imRect.x, pEncCtl->imRect.y, pEncCtl->imRect.width, pEncCtl->imRect.height);*/
    
mutex_unlock:
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}


static int ENC_DrawRect(struct VideoEncoder *pThiz, VideoObjectPacket* pObjPacket)
{
	return 0;
}

/*******************************************************************************
  Function:     ENC_CalcVideoInOptions
  Description:  根据编码属性计算需要的前端输入属性
  Input:        - pThiz: 视频编码接口指针
                - pEncOptions: 需要配置的编码属性
  Output:       - pVinOptions: vi的配置
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
static int ENC_CalcVideoInOptions(struct VideoEncoder *pThiz, 
                                        VideoEncOptions *pEncOptions, 
                                        VideoEncVinOptions* pVinOptions)
{
    int ret; 
    char key[8] = {0};
    VIDEO_picSize_t picSize;   
    VIDEO_resolution_t resolution;

    if ((NULL == pThiz) || (NULL == pEncOptions) || (NULL == pVinOptions))
    {
        LOG_PrintError("Parameter is NULL!\n");
        return -1;
    }

    ret = VIDEO_GetPicSize(videoCtl_g.standard, 
                           videoCtl_g.resolution, 
                           &picSize,
                           0);
    if (ret)
    {
        LOG_PrintError("VIDEO_GetPicSize failed!\n");
        return -1; 
    } 

    /* 默认不修改vi参数 */
    pVinOptions->width = picSize.u32Width;
    pVinOptions->height = picSize.u32Height;
    pVinOptions->fps = videoCtl_g.fps;
    pVinOptions->wideDynamic = videoCtl_g.wdrEnable;
    pVinOptions->needReboot = 0;

    picSize.u32Width = pEncOptions->width;
    picSize.u32Height = pEncOptions->height;

    ret = VIDEO_GetResolution(&picSize, 
                              0, 
                              videoCtl_g.standard,
                              &resolution);
    if (ret)
    {
        LOG_PrintError("VIDEO_GetResolution failed!\n");
        return -1;
    } 
    
    if (videoCtl_g.maxFps)
    {
        pVinOptions->fps = videoCtl_g.maxFps;
    }
    else
    {
        pVinOptions->fps = (VIDEO_STANDARD_PAL==videoCtl_g.standard)?25:30;
    }

    if (pVinOptions->needReboot)
    {
	    picSize.u32Width = pVinOptions->width;
	    picSize.u32Height = pVinOptions->height;
	    VIDEO_GetResolution(&picSize, 0, videoCtl_g.standard, &resolution);		
    
    	sprintf(key, "%d", resolution);
    	INI_KeySet(VIDEO_INI_CFG, "video", "resolution", key);
    }
    
    return 0;
}


void ENC_CombineYuv(unsigned char *pSrc, int srcWidth, int srcHeight, 
                            unsigned char *pDst, int dstWidth, int dstHeight, 
                            int startX, int startY)
{
    int i, j;
    unsigned char *pTmpData;
    unsigned char y;
    
    /*printf("srcWidth=%d,srcHeight=%d,dstWidth=%d,dstHeight=%d,startX=%d,startY=%d\n",
            srcWidth, srcHeight,dstWidth,dstHeight,startX,startY);*/

    for (i = 0; i < srcHeight; i++)//copy y
    {
        for(j = 0; j< srcWidth; j++)
        {
            y = *(pSrc + j + i*srcWidth);
            if(0 == y)//不拷贝, 透明效果
            {
                
            }
            else//拷贝
            {
                *(pDst + startX + (startY+i)*dstWidth + j) = y;
            }
        }
        //pTmpData = pDst + dstWidth*(startY + i) + startX;
        //memcpy(pTmpData, pSrc + i*srcWidth, srcWidth);
    }

   //uv不拷贝，若拷贝则会有osd框的阴影
}


int ENC_HdmiIsEnable()
{
    int iRet =0;
    int HdmiIsEnable = 0;

    if( VIDEO_PlayStatus())
    {
        HdmiIsEnable = 1;
    }
    else
    {
        HdmiIsEnable = 0;
    }

    return HdmiIsEnable;
}

static int ENC_ImageToEncDeal(int iChn,ENC_ctl_t *pEncCtls,ENC_listImageBufInfo_t* pstImageInfo)
{
    int iRet =0;

    if(NULL == pEncCtls || NULL == pstImageInfo)
    {
        return -1;
    }

    if(ENC_START_EMPTY !=  pEncCtls->encState)
    {
        //printf("pEncCtls->encState is %d\n", pEncCtls->encState);
        return -1;
    }

    int iEncChn = pEncCtls->vencChn;

    /* lock */
    pthread_mutex_lock(&videoCtl_g.encodeMutex);

    if(iChn == pEncCtls->iSrcChn) //当前list编号和当前编码通道需要的输入源list匹配
    {
        /* send to enc  */
        //printf("pEncCtls->isCreateVenc is %d\n", pEncCtls->isCreateVenc);
        if(pEncCtls->isCreateVenc)
        {
            if(1 == pEncCtls->iNoneCopy)
            {
                MEDIA_BUFFER stMbBuffer = (MEDIA_BUFFER)pstImageInfo->stImageBufInfo.revered;
                uint64  pts = pstImageInfo->stImageBufInfo.pts;

                RK_MPI_MB_SetTimestamp(stMbBuffer, pts);
                iRet = RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, iEncChn, stMbBuffer);
                

                //printf("RK_MPI_SYS_SendMediaBuffer RK_ID_VENC pts:%llu success\n", pts);
            }
            else
            {
                pEncCtls->iFrameCnt++;
                ENC_ImageBufInfo_t* pstImageBuffer = NULL;
                if(0 == pEncCtls->iFrameCnt%2)
                {
                    pstImageBuffer = &(pEncCtls->stMediaMb[0]);
                }
                else
                {
                    pstImageBuffer = &(pEncCtls->stMediaMb[1]);
                }

                /* resize */
                ENC_ImageRect_t stSrcRect;
                ENC_ImageRect_t stDstRect;

                stSrcRect.wid = pstImageInfo->stImageBufInfo.stRect.wid;
                stSrcRect.hei = pstImageInfo->stImageBufInfo.stRect.hei;
                stSrcRect.wstride = stSrcRect.wid;
                stSrcRect.hstride = stSrcRect.hei;

                stDstRect.wid = pstImageBuffer->stRect.wid;
                stDstRect.hei = pstImageBuffer->stRect.hei;
                stDstRect.wstride = stDstRect.wid;
                stDstRect.hstride = stDstRect.hei;

                MEDIA_BUFFER    stSrcMb = (MEDIA_BUFFER)pstImageInfo->stImageBufInfo.revered;
                MEDIA_BUFFER    stDstMb = (MEDIA_BUFFER)pstImageBuffer->revered;

                int ret = VI_ImageResizeRga(stSrcRect,stDstRect,stSrcMb,stDstMb, RK_FORMAT_YCbCr_420_SP, RK_FORMAT_YCbCr_420_SP);
                if(ret < 0)
                {
                    LOG_PrintError("ENC_ImageResizeRga failed: %d\n",ret);
                }
                uint64  pts = pstImageInfo->stImageBufInfo.pts;

                RK_MPI_MB_SetTimestamp(stDstMb, pts);

                iRet = RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, iEncChn, stDstMb);

            }
        }
        else
        {
            if(1 == pEncCtls->iStretch)
            {
                pEncCtls->iFrameCnt++;
                ENC_ImageBufInfo_t* pstImageBuffer = NULL;
                if(0 == pEncCtls->iFrameCnt%2)
                {
                    pstImageBuffer = &(pEncCtls->stMediaMb[0]);
                }
                else
                {
                    pstImageBuffer = &(pEncCtls->stMediaMb[1]);
                }


                /* resize:16:9 */
                ENC_ImageRect_t stSrcRect;
                ENC_ImageRect_t stDstRect;

                stSrcRect.wid = pstImageInfo->stImageBufInfo.stRect.wid;
                stSrcRect.hei = pstImageInfo->stImageBufInfo.stRect.hei;
                stSrcRect.wstride = stSrcRect.wid;
                stSrcRect.hstride = stSrcRect.hei;

                stDstRect.wid = pstImageBuffer->stRect.wid;
                stDstRect.hei = pstImageBuffer->stRect.hei;
                stDstRect.wstride = stDstRect.wid;
                stDstRect.hstride = stDstRect.hei;
                
                MEDIA_BUFFER    stSrcMb = (MEDIA_BUFFER)pstImageInfo->stImageBufInfo.revered;
                MEDIA_BUFFER    stDstMb = (MEDIA_BUFFER)pstImageBuffer->revered;

                int ret = VI_ImageResizeRga(stSrcRect,stDstRect,stSrcMb,stDstMb, RK_FORMAT_YCbCr_420_SP, RK_FORMAT_YCbCr_420_SP);
                if(ret < 0)
                {
                    LOG_PrintError("ENC_ImageResizeRga failed: %d\n",ret);
                }

                int size = pstImageBuffer->stRect.wid * pstImageBuffer->stRect.hei * 3 / 2;
                RK_MPI_MB_SetSize(stDstMb, size);
                ENC_GetYuv422Stretch(pEncCtls, stDstMb,pEncCtls->encPicSize.u32Width,pEncCtls->encPicSize.u32Height);
            }
            else
            {
                pEncCtls->iFrameCnt++;
                ENC_ImageBufInfo_t* pstImageBuffer = NULL;
                if(0 == pEncCtls->iFrameCnt%2)
                {
                    pstImageBuffer = &(pEncCtls->stMediaMb[0]);
                }
                else
                {
                    pstImageBuffer = &(pEncCtls->stMediaMb[1]);
                }

                /* resize */
                ENC_ImageRect_t stSrcRect;
                ENC_ImageRect_t stDstRect;

                stSrcRect.wid = pstImageInfo->stImageBufInfo.stRect.wid;
                stSrcRect.hei = pstImageInfo->stImageBufInfo.stRect.hei;
                stSrcRect.wstride = stSrcRect.wid;
                stSrcRect.hstride = stSrcRect.hei;

                stDstRect.wid = pstImageBuffer->stRect.wid;
                stDstRect.hei = pstImageBuffer->stRect.hei;
                stDstRect.wstride = stDstRect.wid;
                stDstRect.hstride = stDstRect.hei;
                
                MEDIA_BUFFER    stSrcMb = (MEDIA_BUFFER)pstImageInfo->stImageBufInfo.revered;
                MEDIA_BUFFER    stDstMb = (MEDIA_BUFFER)pstImageBuffer->revered;

                int ret = VI_ImageResizeRga(stSrcRect,stDstRect,stSrcMb,stDstMb, RK_FORMAT_YCbCr_420_SP, RK_FORMAT_YCbCr_420_SP);
                if(ret < 0)
                {
                    LOG_PrintError("ENC_ImageResizeRga failed: %d\n",ret);
                }

                //iRet = RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, iEncChn, stDstMb);
                int size = pstImageBuffer->stRect.wid * pstImageBuffer->stRect.hei * 3 / 2;
                RK_MPI_MB_SetSize(stDstMb, size);
                ENC_GetYuv422(pEncCtls, stDstMb);
           }
        }
    }

    /* unlock */
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);

    return iRet;
}


static int ENC_SendImageBufferToEnc(int iChn,ENC_listImageBufInfo_t* pstImageInfo)
{
    int iRet =0;
    ENC_ctl_t *pEncCtls = NULL;

    for(int i=0; i<VENC_MAX_CHN_NUM; i++) //遍历看当前list发给哪个编码通道
    {
        pEncCtls = videoCtl_g.pEncCtls[i];

        if(pEncCtls)
        {    
            ENC_ImageToEncDeal(iChn,pEncCtls,pstImageInfo);
        }

    }

    return iRet;
}


static int ENC_SendImageBufferToVo(int iChn,ENC_listImageBufInfo_t* pstImageInfo)
{
    int iRet = 0;
    MOD_ID_E enModID = RK_ID_VO;
    RK_S32  s32ChnID = 0;
    MEDIA_BUFFER buffer = (MEDIA_BUFFER)pstImageInfo->stImageBufInfo.revered;

    static long long ts_start = 0;
    static int iFrameCount = 0;
    long long ts_now;
    int iShowFps = 1;

    if( 1 == ENC_HdmiIsEnable() )
    {
        iRet = RK_MPI_SYS_SendMediaBuffer(enModID, s32ChnID, buffer);
        if (iRet)
        {
            LOG_PrintError("Fail to RK_MPI_SYS_SendMediaBuffer with %d\n", iRet);
            return -1;
        }

        //printf("RK_MPI_SYS_SendMediaBuffer RK_ID_VO chn:%d success\n", iChn);

        if(1 == iShowFps)
        {
            int iPeriod = 30; /* ms */
            iFrameCount++;
            ts_now = vdo_get_time();
            if(ts_now - ts_start > iPeriod*1000)
            {
                LOG_PrintInfo("vo-mod%d-chn%d, %0.2f fps in %ds,count %d \n",enModID,iChn,(float)1.0*iFrameCount/iPeriod,
                    iPeriod,
                    iFrameCount
                    );
                ts_start = ts_now;
                iFrameCount = 0;
            }
        }
    }

    return iRet;
}

static int ENC_SendImageBuffer(int iChn,ENC_listImageBufInfo_t* pstImageInfo)
{
    int iRet = 0;

    if(VIDEO_STREAM_1080P_LIST_CHN == iChn) //1080p可以同时发给有需要的多个编码通道，下同
    {
        ENC_SendImageBufferToEnc(iChn,pstImageInfo);

        ENC_SendImageBufferToVo(iChn,pstImageInfo);
    }
    else if(VIDEO_STREAM_720P_LIST_CHN == iChn)
    {
        ENC_SendImageBufferToEnc(iChn,pstImageInfo);
    }

    return iRet;
}
static int ENC_ListMediaProcess(int iChn)
{
    /* get  buffer */
    int iRet = 0;
    static int isSavePic = 0;
    ENC_listImageBufInfo_t stImageBuffe;
    memset(&stImageBuffe,0x0,sizeof(ENC_listImageBufInfo_t));

    iRet = Stream_ListGetFullNode(iChn,&stImageBuffe);
    if(iRet)
    {
        return -1;
    }

    /* resize for to buffer*/
    ENC_SendImageBuffer(iChn,&stImageBuffe);

    if(1 == isSavePic)
    {
        char path[64];
        sprintf(path,"/tmp/zhengyc/image_list_chn%d.yuv",iChn);
        int size = stImageBuffe.stImageBufInfo.stRect.wid * stImageBuffe.stImageBufInfo.stRect.hei *3/2;
        void* pVir = RK_MPI_MB_GetPtr((MEDIA_BUFFER)stImageBuffe.stImageBufInfo.revered);
        vdo_writeToFile(path,size,pVir);

        isSavePic = 0;
    }

    /* put buffer */
    iRet = Stream_ListPutEmptyNode(iChn,&stImageBuffe);
    if(iRet)
    {
        return -1;
    }

    return iRet;
}


int Enc_StremStatus(void)
{
    int iUvcStreamStatus = 0;
    int iUvcEncChn = 0;

    ENC_ctl_t *pEncCtls = videoCtl_g.pEncCtls[iUvcEncChn];
    if(NULL != pEncCtls)
    {
        if(ENC_START_EMPTY == pEncCtls->encState)
        {
            iUvcStreamStatus = 1;
        }
    }

    return iUvcStreamStatus;
}


int ENC_LedIsShow()
{
    int iShow = 0;

    /* hdmi or uvc enc or ndi enc or cover*/
    if(0 == VI_IsCover())
    {
        if(VIDEO_PlayStatus() || Enc_StremStatus() )
        {
            iShow = 1;
        }
        else
        {
            iShow = 0;
        }
    }
    else
    {
        iShow = 0;
    }

    return iShow;
}



/*******************************************************************************
  Function:     ENC_ImageBufToEnc
  Description:  将NV12视频数据送给编码或yuyv
  Input:        - pEncCtl: 编码控制指针
                - srcMb: NV12视频数据指针
                - stSrcRect: NV12视频宽高信息
  Output:       无              
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
//#define TEST_FRAMERATE
#ifdef TEST_FRAMERATE 
    static unsigned long lastPacketTime = 0;
    static unsigned long packetNums = 0;
    static unsigned long allPacketTimes = 0;
    static unsigned long allPacketNums = 0;
#endif

int ENC_ImageBufToEnc(ENC_ctl_t *pEncCtl, MEDIA_BUFFER srcMb, ENC_ImageRect_t stSrcRect)
{  
    if (!pEncCtl)
    { 
        LOG_PrintError("pEncCtl is NULL !\n"); 
        return -1;
    }

	VENC_CHN_STATUS_S stStatus;

    MEDIA_BUFFER sendMb = NULL;
    
    unsigned int encWidth = pEncCtl->vencAttr.stVencAttr.u32PicWidth;
    unsigned int encHeight = pEncCtl->vencAttr.stVencAttr.u32PicHeight;

    if((encWidth != stSrcRect.wid) 
        || (encHeight != stSrcRect.hei))
    {

        /*LOG_PrintWarn("stSrcRect.wid=%d, stSrcRect.hei=%d,encWidth=%d,encHeight=%d, oldWidth_s=%d, oldHeight_s=%d\n", 
                    stSrcRect.wid, stSrcRect.hei, encWidth, encHeight, oldWidth_s, oldHeight_s);*/ 

        if((encWidth != pEncCtl->oldWidth_s) 
            || (encHeight != pEncCtl->oldHeight_s))
        {
            if(pEncCtl->encMb[0])
            {
                RK_MPI_MB_ReleaseBuffer(pEncCtl->encMb[0]);
            }

            MB_IMAGE_INFO_S stImageInfo = {0};
            stImageInfo.enImgType = IMAGE_TYPE_NV12;
            stImageInfo.u32Width = encWidth;
            stImageInfo.u32Height = encHeight;
            stImageInfo.u32HorStride = stImageInfo.u32Width;
            stImageInfo.u32VerStride = stImageInfo.u32Height;
            pEncCtl->encMb[0] = RK_MPI_MB_CreateImageBuffer(&stImageInfo, RK_TRUE, 0);
            if (!pEncCtl->encMb[0])
            {
                LOG_PrintError("RK_MPI_MB_CreateBuffer failed !\n");
                return -1;
            }

            if(pEncCtl->encMb[1])
            {
                RK_MPI_MB_ReleaseBuffer(pEncCtl->encMb[1]);
            }

            pEncCtl->encMb[1] = RK_MPI_MB_CreateImageBuffer(&stImageInfo, RK_TRUE, 0);
            if (!pEncCtl->encMb[1])
            {
                LOG_PrintError("RK_MPI_MB_CreateBuffer failed !\n");
                return -1;
            }
        }

        /* 2块buffer交替使用，解决闪屏问题 */  
        sendMb = pEncCtl->encMb[pEncCtl->mbIndex % 2];        
        pEncCtl->mbIndex++;
        
        /* resize */
        ENC_ImageRect_t stDstRect;
        stDstRect.wid = encWidth;
        stDstRect.hei = encHeight;
        stDstRect.wstride = stDstRect.wid;
        stDstRect.hstride = stDstRect.hei;

        int iRet = VI_ImageResizeRga(stSrcRect, stDstRect, srcMb, sendMb, RK_FORMAT_YCbCr_420_SP, RK_FORMAT_YCbCr_420_SP);
        if(iRet < 0)
        {
            LOG_PrintError("VI_ImageResizeRga failed !\n"); 
            return -1;
        }

        pEncCtl->oldWidth_s = encWidth;
        pEncCtl->oldHeight_s = encHeight;
    }
    else
    {
        sendMb = srcMb;
    }
    
    if (pEncCtl->isCreateVenc)
    {
        if(pEncCtl->encState == ENC_START_EMPTY)
        {
            RK_MPI_VENC_QueryStatus(pEncCtl->vencChn, &stStatus);
            if(stStatus.u32LeftFrames < 2){
                int ret = RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, pEncCtl->vencChn, sendMb);
                if (ret)
                {
                    LOG_PrintError("Fail to RK_MPI_SYS_SendMediaBuffer with %d", ret);
                    return -1;
                }

        #ifdef TEST_FRAMERATE   
            unsigned long currentTime = vdo_get_time();
            if (lastPacketTime != 0 && currentTime - lastPacketTime > 10000){
                allPacketTimes++;
                if (allPacketTimes != 0){
                    printf("Average second receive video packetnum: %.1f \n", (float)allPacketNums / allPacketTimes / 10);
                }
                lastPacketTime = currentTime;
                packetNums=0;
            } else if (lastPacketTime == 0){
                lastPacketTime = currentTime;
            }
            packetNums++;
            allPacketNums++;
        #endif 
            }
        }
    }
    else
    {
        if(pEncCtl->encState == ENC_START_EMPTY)
        {
            pEncCtl->encPicSize.u32Width = encWidth;
            pEncCtl->encPicSize.u32Height = encHeight;
            RK_U32 size = encWidth * encHeight * 3 / 2;
            RK_MPI_MB_SetSize(sendMb, size);

            if((640 == pEncCtl->encPicSize.u32Width) && (480 ==  pEncCtl->encPicSize.u32Height) )
            {
                ENC_GetYuv422Stretch(pEncCtl, sendMb, encWidth, encHeight);
            }
            else
            {
                ENC_GetYuv422(pEncCtl, sendMb);
            }
        }
    }

#if 0    
    if(count_s % 100 == 1)
    {
        LOG_PrintError("RK_MPI_SYS_SendMediaBuffer RK_ID_VENC success,pEncCtl->vencChn:%d,stSrcRect.wid=%d, stSrcRect.hei=%d,encWidth=%d,encHeight=%d\n", 
                    pEncCtl->vencChn, stSrcRect.wid, stSrcRect.hei, encWidth, encHeight); 
    }          
#endif
        
    return 0;
}

/*******************************************************************************
  Function:     createVideoEncoder
  Description:  创建编码通道接口
  Input:        - pEncDesc: 编码通道接口参数
  Output:       - ppVideoEncoder: 编码通道接口
  Return:       0: 操作成功
                -1: 操作失败
  Others:       无
*******************************************************************************/
int PAL_API createVideoEncoder(VideoEncDesc *pEncDesc, 
                               VideoEncoder **ppVideoEncoder)
{
    int ret = 0, result = 0;
    unsigned int ch = 0;
    VideoEncoder *pVideoEncoder = NULL;
    unsigned int streamId = 0;
    ENC_ctl_t *pEncCtl = NULL;
    VIDEO_resolution_t resolution = videoCtl_g.resolution;

    if ((NULL == pEncDesc) || (NULL == ppVideoEncoder))
    {
        LOG_PrintError("Parameter is NULL!\n");
        return -1;
    }
#if 0
    for (ch = 0; ch < 32; ch++)
    {
        if ((pEncDesc->srcs & (1 << ch)) != 0)
        {
            break;
        }
    }

    if (ch >= videoCtl_g.viChnNum)
    {
        LOG_PrintError("Video Channel (%d) is out of range!\n", ch);
        return -1;
    }

    if ((pEncDesc->stream >= videoEncStreamNumber) ||
         (ENC_UNSUPPORT_STREAM == encStreamMap_s[pEncDesc->stream]))
    {
        LOG_PrintInfo("Video stream (%d) is not supported!\n", 
                        pEncDesc->stream);
        return -1;
    }

    streamId = ch * ENC_STREAM_PER_CHN + encStreamMap_s[pEncDesc->stream];
#endif

    streamId = pEncDesc->channel;
    if(streamId > VENC_MAX_CHN_NUM)
    {
        LOG_PrintError("VideoEnc Channel (%d) is out of range!\n", streamId);
        return -1;
    }

    pthread_mutex_lock(&videoCtl_g.encodeMutex);
    
    pEncCtl = videoCtl_g.pEncCtls[streamId];
    if (pEncCtl)
    {
        LOG_PrintInfo("video encoder has already been created,"
                       "srcs = %d, stream = %d\n", 
                       (int)pEncDesc->srcs, pEncDesc->channel);
        pEncCtl->refCount++;
        *ppVideoEncoder = &pEncCtl->videoEncoder;
        goto mutex_unlock;
    }
    

    pEncCtl = (ENC_ctl_t *)malloc(sizeof(ENC_ctl_t));
    if (!pEncCtl)
    {
        LOG_PrintError("There is no enough memory to allocate VideoEncoder!\n");
        ret = -1;
        goto mutex_unlock;
    }
    memset(pEncCtl, 0, sizeof(ENC_ctl_t));

    pEncCtl->streamId = streamId;
    pEncCtl->encStream = 0;
    pEncCtl->encType = videoEncH264;
    pEncCtl->refCount++;
    pEncCtl->screenMode = 0;
    pEncCtl->encState = ENC_STOP; 
    pEncCtl->vencChn = streamId;


    result = ENC_GetCfg(pEncCtl);
    if (result)
    {
        LOG_PrintError("Fail to get encode config!\n");
        ret = -1;
        goto free_ctl;
    }

#if 0
    pEncCtl->objPacket.objects = pEncCtl->videoObj;
#endif

#ifdef CALL_BACK
    resolution = VIDEO_RESOLUTION_H1080;
    int i;
    VIDEO_picSize_t picSize;
    VIDEO_GetPicSize(videoCtl_g.standard, 
                         resolution, 
                         &picSize,
                         videoCtl_g.rotate);
    pEncCtl->maxLen = picSize.u32Width*picSize.u32Height;
    INIT_LIST_HEAD(&pEncCtl->emptyBuf);
    INIT_LIST_HEAD(&pEncCtl->fullBuf);
    pthread_mutex_init(&pEncCtl->bufLock, NULL);
    for (i = 0; i < 5; i++)
    {
        pEncCtl->listBuf[i].pBuf = (char *)malloc(pEncCtl->maxLen);
        if (!pEncCtl->listBuf[i].pBuf)
        {
            LOG_PrintError("Fail to malloc!\n");
            ret = -1;
            goto free_ctl;
        }
	    list_add_tail(&pEncCtl->listBuf[i].list, &pEncCtl->emptyBuf);
	}
#endif
    
    videoCtl_g.pEncCtls[streamId] = pEncCtl;
    
    pVideoEncoder = &pEncCtl->videoEncoder;
    pVideoEncoder->priv = pEncCtl;
    pVideoEncoder->addRef = ENC_AddRef;
    pVideoEncoder->release = ENC_Release;
    pVideoEncoder->getChipId = ENC_GetChipId;
    pVideoEncoder->getChipCaps = ENC_GetChipCaps;
    pVideoEncoder->getCaps = ENC_GetCaps;
    pVideoEncoder->forceIFrame = ENC_ForceIFrame;
    pVideoEncoder->setFormat = ENC_SetFormat;
    pVideoEncoder->start = ENC_Start;
    pVideoEncoder->stop = ENC_Stop;
    pVideoEncoder->setTitle = ENC_SetTitle;
    pVideoEncoder->setCover = ENC_SetCover;
    pVideoEncoder->getPacket = ENC_GetPacket;
    pVideoEncoder->releasePacket = ENC_ReleasePacket;
    pVideoEncoder->setWaterMark = ENC_SetWatermark;
    pVideoEncoder->setROI = ENC_SetRoi; 
    pVideoEncoder->calcVideoInOptions = ENC_CalcVideoInOptions;
	pVideoEncoder->requestPacket = ENC_RequestPacket;
    pVideoEncoder->setEncodeCrop = ENC_SetCrop;
    pVideoEncoder->getEncodeCrop = ENC_GetCrop;
    pVideoEncoder->drawRect = ENC_DrawRect;
    pVideoEncoder->setEncodeStitch = ENC_SetStitch;
    pVideoEncoder->setScreenMode = ENC_SetScreenMode;

    *ppVideoEncoder = &pEncCtl->videoEncoder;
    
    LOG_PrintInfo("Create vencChn:%d successfully!\n", pEncCtl->vencChn);
    goto mutex_unlock;
    
free_ctl: 
    free(pEncCtl);
    
mutex_unlock:
    pthread_mutex_unlock(&videoCtl_g.encodeMutex);
    return ret;
}


/*******************************************************************************
  Function:     getEncoderFormat
  Description:  获取各路径路流支持的分辨率，
                该接口需要在配置sensor制式和分辨率后配置
  Input:        - encoderFormat: 各路码流支持的分辨率列表
  Output:       无
  Return:       0: 操作成功
                <0: 操作失败
  Others:       无
*******************************************************************************/
int PAL_API getEncoderFormat(VideoEncoderFormat *pEncoderFormat)
{
    int i=0;  
    
    if (!pEncoderFormat)
    {
        LOG_PrintError("Params is NULL!\n");
    } 

    memset(pEncoderFormat, 0, sizeof(VideoEncoderFormat));

    i = 0;

    switch(videoCtl_g.resolution)
    { 
        case VIDEO_RESOLUTION_H1080:
        	if (videoCtl_g.cpuType == SYS_CPU_HI3516EV200)
        	{
        		pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                          videoResolution_1080P;

			  	pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
														  videoResolution_1080P;

                memcpy(pEncoderFormat->rotateResolutionLimit, 
                       pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                       sizeof(VideoEncStream)*8);
        	}
        	else
        	{
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                          videoResolution_1080P;
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                           videoResolution_1_3M;
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                           videoResolution_720P;
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                             videoResolution_D1;  

				memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
						 pEncoderFormat->encodeResolutions[videoEncStreamMain], 
						 sizeof(VideoEncStream)*8);
				pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
															 videoResolution_CIF;  

                 memcpy(pEncoderFormat->rotateResolutionLimit, 
                        pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                        sizeof(VideoEncStream)*8);
                 
                 pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;  
        	}
            break;
        case VIDEO_RESOLUTION_H1536:
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_3M;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_1_3M;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;

            memcpy(pEncoderFormat->rotateResolutionLimit, 
                   pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                   sizeof(VideoEncStream)*8);

            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;
            break;
        case VIDEO_RESOLUTION_H1520:          
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_2048_1520; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_1_3M;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;

            memcpy(pEncoderFormat->rotateResolutionLimit, 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;         
            
            
            break;
        case VIDEO_RESOLUTION_2304_1296:   
            if (videoCtl_g.cpuType == SYS_CPU_HI3516EV200)
            {
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_2304_1296;  
                                                                 
                pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                            videoResolution_CIF;
                                                            
                pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                          videoResolution_640_360;
                
                memcpy(pEncoderFormat->rotateResolutionLimit, 
                       pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                       sizeof(VideoEncStream)*8);
            }
            else
            {
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_2304_1296;  
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                          videoResolution_1080P; 
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                           videoResolution_1_3M;
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                           videoResolution_720P;
                pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                             videoResolution_D1; 
                                                                 
                memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                        pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                        sizeof(VideoEncStream)*8);
                pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                            videoResolution_CIF;
                                                            
                memcpy(pEncoderFormat->rotateResolutionLimit, 
                       pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                       sizeof(VideoEncStream)*8); 
                pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;
            }
            break;
        case VIDEO_RESOLUTION_4M: 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_2592_1520;  
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                          videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                           videoResolution_1_3M;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                           videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                             videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;
                                                        
            memcpy(pEncoderFormat->rotateResolutionLimit, 
                   pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                   sizeof(VideoEncStream)*8);
            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;
            break;
        case VIDEO_RESOLUTION_2560_1440:   
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_2560_1440; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_1_3M;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;
                                                        
            memcpy(pEncoderFormat->rotateResolutionLimit, 
                   pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                   sizeof(VideoEncStream)*8);
            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;
            break;
        case VIDEO_RESOLUTION_H1920:      
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_2560_1920; 
             pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_2560_1440; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_3M; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_1_3M;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;
                                                        
            memcpy(pEncoderFormat->rotateResolutionLimit, 
                   pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                   sizeof(VideoEncStream)*8);
            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;
            break;
        case VIDEO_RESOLUTION_2592_1944:      
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_2592_1944; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_2560_1440; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_3M; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;
                                                        
            memcpy(pEncoderFormat->rotateResolutionLimit, 
                   pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                   sizeof(VideoEncStream)*8);
            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;
            break;
		case VIDEO_RESOLUTION_4096_2160:
			pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_4096_2160; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_2592_1520;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_1_3M;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;
                                                        
            memcpy(pEncoderFormat->rotateResolutionLimit, 
                   pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                   sizeof(VideoEncStream)*8);
            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;	
			break;
	    case VIDEO_RESOLUTION_3840_2160:
			pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
			                                      videoResolution_3840_2160;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_2592_1520;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_1_3M;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;
                                                        
            memcpy(pEncoderFormat->rotateResolutionLimit, 
                   pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                   sizeof(VideoEncStream)*8);
            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;	
			break;
	    case VIDEO_RESOLUTION_4000_3000:
			pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
			                                      videoResolution_4000_3000; 
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_4096_2160;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                  videoResolution_3840_2160;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                      videoResolution_1080P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                       videoResolution_720P;
            pEncoderFormat->encodeResolutions[videoEncStreamMain][i++] = 
                                                         videoResolution_D1; 

            memcpy(pEncoderFormat->encodeResolutions[videoEncStreamSnapshot], 
                    pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                    sizeof(VideoEncStream)*8);
            pEncoderFormat->encodeResolutions[videoEncStreamSnapshot][i] = 
                                                        videoResolution_CIF;
                                                        
            memcpy(pEncoderFormat->rotateResolutionLimit, 
                   pEncoderFormat->encodeResolutions[videoEncStreamMain], 
                   sizeof(VideoEncStream)*8);
            pEncoderFormat->rotateResolutionLimit[i] = videoResolution_CIF;	
			break;
        default:
            LOG_PrintError("sensor resolution %d is invalid!\n", videoCtl_g.resolution);
            return -1;
    } 
    
    i = 0;
    if (videoCtl_g.cpuType == SYS_CPU_HI3516EV200)
    {
        pEncoderFormat->encodeResolutions[videoEncStreamExtra1][i++] = 
                                                        videoResolution_640_360;
    }
    else
    {
        pEncoderFormat->encodeResolutions[videoEncStreamExtra1][i++] = 
                                                             videoResolution_D1;
    }
    
 	pEncoderFormat->encodeResolutions[videoEncStreamExtra1][i++] = 
	                                                        videoResolution_CIF; 		

    i = 0;
    pEncoderFormat->encodeResolutions[videoEncStreamExtra2][i++] = 
                                                           videoResolution_720P;  
    pEncoderFormat->encodeResolutions[videoEncStreamExtra2][i++] = 
                                                            videoResolution_VGA;  
    pEncoderFormat->encodeResolutions[videoEncStreamExtra2][i++] = 
                                                            videoResolution_CIF; 
                                                            
    switch (videoCtl_g.standard)
    {
        case videoStandardPAL:
            pEncoderFormat->defaultFps[videoEncStreamMain] = 25;
            pEncoderFormat->defaultFps[videoEncStreamExtra1] = 25;
            pEncoderFormat->defaultFps[videoEncStreamExtra2] = 25;
            pEncoderFormat->defaultFps[videoEncStreamSnapshot] = 1;
            break;

        case videoStandardNTSC:
            pEncoderFormat->defaultFps[videoEncStreamMain] = 30;
            pEncoderFormat->defaultFps[videoEncStreamExtra1] = 30;
            pEncoderFormat->defaultFps[videoEncStreamExtra2] = 30;
            pEncoderFormat->defaultFps[videoEncStreamSnapshot] = 1;
            break;

        default:
            LOG_PrintError("standard %d is invalid!\n", videoCtl_g.standard);
            return -1;
    }

    /* 配置帧率必须小于sensor帧率 */
    for (i = 0; i < videoEncStreamNumber; i++)
    {
        if (pEncoderFormat->defaultFps[i] > videoCtl_g.fps)
        {
            pEncoderFormat->defaultFps[i] = videoCtl_g.fps;
        } 
    }

    return 0;
}
